Skip to content

Add support for React router v8 - #11289

Open
smeng9 wants to merge 57 commits into
marmelab:nextfrom
smeng9:master
Open

Add support for React router v8#11289
smeng9 wants to merge 57 commits into
marmelab:nextfrom
smeng9:master

Conversation

@smeng9

@smeng9 smeng9 commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Problem

react-router v8 is out

Solution

Follow #10440

Add support for React router v8

Find a way to avoid dependency resolution issue after dropping react-router-dom

How To Test

Build RA packages locally, then link with apps in /examples folder

Additional Checks

  • The PR targets master for a bugfix or a documentation fix, or next for a feature
    next branch seems is disappeared, I assume master branch can be used for new features.
  • The PR includes unit tests (if not possible, describe why)
    Needs to be end-2-end tested with /examples folder.
  • The PR includes one or several stories (if not possible, describe why)
    Stories using the routers are updated.
  • The documentation is up to date

Also, please make sure to read the contributing guidelines.

@fzaninotto

Copy link
Copy Markdown
Member

Thanks for your PR, but U'm not fan of the extra compatibility layer & the partial reimplementation. I'd prefer if you created an entirely new adapter for react-router v8 (in a new package). We'd probably need to move the current routing adapter to a package, too.

@smeng9

smeng9 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Hi @fzaninotto

The ra-router-react-router-v8 is pulled out to a separate package

@fzaninotto fzaninotto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Several remarks:

  • This is a new feature, so it must be PRed against nextand not master
  • As RR v7 is still loaded by default by ra-core, users of RRv8 will end up with 2 versions of RR, leading to probable context conflicts. You also need to extract the default RRv7 adapter to a package in the monorepo
  • The new RRv8 adapter needs unit tests in addition to user stories
  • you also need to test the basename feature (check the tests of the tanstack router adapter)

Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
@smeng9

smeng9 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Hi @fzaninotto

Can you restore the next branch, then I can target this merge request against the correct branch? This branch is probably accidentally deleted in your repo.

I have already noted this in the additional checks.

@fzaninotto

Copy link
Copy Markdown
Member

Sure, the next branch is now up again.

@smeng9
smeng9 changed the base branch from master to next June 24, 2026 19:31
@smeng9 smeng9 changed the title Add support for React router v8 Draft: Add support for React router v8 Jun 25, 2026
@smeng9
smeng9 marked this pull request as draft June 25, 2026 02:21
@smeng9 smeng9 changed the title Draft: Add support for React router v8 Add support for React router v8 Jun 25, 2026
@smeng9
smeng9 requested a review from fzaninotto June 26, 2026 03:04
@smeng9

smeng9 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Hi @fzaninotto

Back to RFR.

@smeng9
smeng9 marked this pull request as ready for review June 26, 2026 04:43
@smeng9 smeng9 changed the title Add support for React router v8 Add support for React router v8 [RFR] Jul 13, 2026
@fzaninotto fzaninotto added the RFR Ready For Review label Jul 30, 2026
@fzaninotto fzaninotto changed the title Add support for React router v8 [RFR] Add support for React router v8 Jul 30, 2026
Shaoyu Meng and others added 11 commits September 4, 2026 11:06
React Router v8 merged react-router-dom into react-router and requires
React 19. Rather than adding a compatibility layer and partial
reimplementation inside ra-core (per review feedback on marmelab#11289), v8
support ships as a standalone, opt-in adapter package mirroring
ra-router-tanstack.

The adapter is a thin pass-through over react-router v8's native API:
the obsolete v6/v7 `future` flags are dropped (now defaults in v8) and
imports come from `react-router` instead of `react-router-dom`. ra-core
keeps its built-in react-router v6/v7 adapter as the default, so existing
apps are unaffected.

The package is ESM-only because react-router v8 is ESM-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
After merging the ra-router-react-router-v8 package, the in-core
react-router adapter no longer needs the compatibility shims:

- reactRouterProvider imports everything from `react-router`, with only
  `Link` and `createHashRouter` from `react-router-dom`
- Form.stories uses `HashRouter` from `react-router-dom` directly
- Remove CompatHashRouter, CompatLink and their tests
- Restore `react-router-dom` to the dependencies of ra-core,
  ra-ui-materialui and react-admin, and narrow the react-router range
  back to ^6.28.1 || ^7.1.1 (v8 is supported via the separate package)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
No code in ra-ui-materialui imports react-router-dom; all routing
imports come from react-router. ra-core declares its own
react-router-dom peer for its router adapter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Individual zshy builds had rewritten the exports field to the flat
pre-normalization form. Restore the nested import/require form that
update-package-exports produces and that matches upstream.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-exports

update-package-exports forces dual import/require exports on every
package; the ESM-only v8 adapter must keep its ESM-only exports (it has
no .cjs build). Also keep minor README/story/doc wording.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Shaoyu Meng and others added 26 commits September 4, 2026 11:10
The resolvePath helper inside useNavigate carries a two-line lead comment
plus an inner "Don't prepend if path already includes basename" comment in
the tanstack adapter, unlike the one-line form inside Link. Align both
router adapters so each call site matches its tanstack counterpart exactly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pter

Drop the extra basenameRef; useNavigate now reads basename from the hook
and lists it in the useCallback dependency array, matching the tanstack
adapter's shape. The navigateRef (react-router-specific, issue marmelab#7634)
stays. Behavior unchanged; 107/107 in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ters

Follow the tanstack adapter's useNavigate skeleton exactly: numeric
(go back/forward) branch first, then the resolvePath helper, then the
object branch, then the string path — with the same comments. Only the
call differs (navigateRef.current(to) instead of router.history.go(to))
since react-router's navigate handles all three input shapes. Keeps the
three router adapters diff-aligned.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Match the tanstack adapter's Link: branch on the object form first
(`if (typeof to === 'object' && to !== null)`) and fall back to
`resolvePath(to as string)` in the else, instead of a nested ternary.
Behavior unchanged; 107/107 in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Build a resolvedPath string via the tanstack Navigate shape (string vs
object branch, append search/hash, then the inline basename-prepend
block) and render <Navigate to={resolvedPath} replace state />. Drops the
to.state merge since react-router's Navigate `to` (a Path) has no state
field — state stays a prop. 107/107 in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pass through the rest of the Navigate props ({...rest}) instead of
listing replace/state explicitly, and drop the "(like react-router does)"
aside from the basename comment. 107/107 in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename the provider-local useProviderBasename to useBasename and publish
the basename via BasenameContext in both RouterWrapper branches. Fix
quote style to satisfy prettier. 107/107 in the react-router adapter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use fireEvent.click for navigation in both router adapter specs, matching
the tanstack provider spec's convention, and keep userEvent.setup/user.type
only in the two unsaved-changes form tests that need typing. 107/107 in
both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Restore the tanstack import ordering (PathlessLayoutRoutes before
NestedResourcesPrecedence) in both router adapter specs. No behavior
change; 107/107 in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… parity)

The earlier blanket fireEvent.click conversion wrongly flipped the one
test that tanstack drives with user.click ("navigate back to parent app").
Match tanstack per-test: user.click + userEvent.setup there, fireEvent.click
everywhere else. 107/107 in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the "// Wait for data to load before clicking" comment that tanstack
has between the two findByText calls in the navigate-within-nested-routes
test, in both router adapter specs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove the extra explanatory comment atop the matchPath block and reword
the useCanBlock test to "should return true for React Router", matching
the tanstack spec wording. 107/107 in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename to "should decode only path separator in URL-encoded splat values"
in both router adapter specs. 107/107.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename the test to "should decode only path separator in URL-encoded
params", restore tanstack's "// UTF-8 characters: 衣類/衣類 encoded" comment,
and drop the react-router-specific note. 107/107 in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename the three tests to match the tanstack useBlocker wording: "should
block navigation when form is dirty", "should allow navigation when
clicking proceed", and "should not block navigation when form is not
dirty". 107/107 in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "should block navigation when form is dirty" test was conflating the
block (confirm fires) with the cancel outcome (declining keeps you on the
form). Split into separate "should block navigation when form is dirty"
and "should cancel navigation when clicking cancel" tests, ordered like
tanstack (block, proceed, cancel, not-dirty). 108/108 in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Match the tanstack useBlocker tests, which have no inline comments in the
test bodies. 108/108 in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er-dom

Part of the React Router v8 migration: v8 merged react-router-dom into
react-router. Move Route/Routes/RouterProvider/BrowserRouter doc imports
to react-router, while keeping createBrowserRouter and Link on
react-router-dom for v6 compatibility.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BrowserRouter is not exported from the react-router core package in v6
(it is DOM-only, like Link and createBrowserRouter). Revert the two
BrowserRouter imports back to react-router-dom; Routes/Route stay on
react-router.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`NODE_OPTIONS=--experimental-vm-modules` is process-global, so setting it on
the shared `test-unit`/`test-unit-ci` scripts pushed the entire default
(CommonJS) jest project into ESM mode. Jest then honored the `"type": "module"`
field of transformed dependencies and refused to `require()` them, so every
suite that transitively imports an ESM-only dep (e.g. react-hotkeys-hook, pulled
in via the layout/button/form barrels) failed to load with "Must use import to
load ES Module". This took out 93 suites across ra-ui-materialui, react-admin,
ra-input-rich-text and ra-i18n-polyglot — the suites failed to run while their
individual tests never executed.

Keep the root config a single CommonJS project (no `projects` array, no flag)
and run ra-router-react-router-next's ESM/React 19 tests as a separate jest
invocation against its own jest.config.cjs, appended to the test scripts with
the flag set only for that run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-next

The package imports react-router at runtime, so it belongs in `dependencies`
(matching the sibling ra-router-react-router), not only in dev/peer deps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ra-core is provided by the react-admin meta-package at runtime; the sibling
ra-router-react-router does not declare it either.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Restore ra-core as a peerDependency of ra-router-react-router-next (it is
provided by the host app, like the other ra-router adapters), and declare
react-router as a peerDependency of ra-no-code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
react-admin imports from react-router (via ra-router-react-router), not
react-router-dom directly, so the devDependency is unnecessary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The auth spec asserted on the post-logout/post-login URL with
cy.url().then(url => expect(url).to.contain(...)), which evaluates the
assertion exactly once before the redirect has necessarily landed,
causing intermittent CI failures. Switch to the retrying cy.url().should()
form already used elsewhere in the file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@smeng9

smeng9 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Rebased after next branch is pointing to 5.15.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RFR Ready For Review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants